package com.tul.vapp.servicecenter.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.tul.cusa.common.log.LogTracerFactory;
import com.tul.cusa.common.log.intf.ILogTracer;
import com.tul.cusa.common.util.DateUtil;
import com.tul.cusa.common.util.MapUtil;
import com.tul.cusa.common.util.StringUtil;
import com.tul.vapp.servicecenter.common.CharSetConstant;
import com.tul.vapp.servicecenter.dao.IAppDao;
import com.tul.vapp.servicecenter.dao.IConfigDao;
import com.tul.vapp.servicecenter.dao.IItemDao;
import com.tul.vapp.servicecenter.dao.INamespaceDao;
import com.tul.vapp.servicecenter.entity.App;
import com.tul.vapp.servicecenter.entity.ConfigBeanVo;
import com.tul.vapp.servicecenter.entity.Item;
import com.tul.vapp.servicecenter.entity.Namespace;
import com.tul.vapp.servicecenter.entity.Status;
import com.tul.vapp.servicecenter.service.IConfigService;
import com.tul.vapp.servicecenter.util.ConfigJsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * create by wujunlei
 *
 * @author wujunlei
 * @since 2018/1/5
 */
@Service
public class ConfigService implements IConfigService {

    /**
     * log
     */
    private static final ILogTracer TRACER = LogTracerFactory.getTracer(ConfigService.class);

    /**
     * format
     */
    private static final String DATE_FORMAT = "yyyyMMddHHmmss";
    /**
     * ems
     */
    private static final String EMS_APP_ID = "ems-proxy";
    /**
     * configDao
     */
    @Autowired
    private IConfigDao configDao;

    /**
     * appDao
     */
    @Autowired
    private IAppDao appDao;

    /**
     * nameSapceDao
     */
    @Autowired
    private INamespaceDao namespaceDao;

    /**
     * itemDao
     */
    @Autowired
    private IItemDao itemDao;

    /**
     * 从操作表导出当前配置
     *
     * @return 文件路径
     */
    @Override
    public String exportConifgData() {
        List<Map<String, Object>> list = configDao.findAllItems();
        List<Map<String, Object>> appList = new ArrayList<Map<String, Object>>();
        if (list != null) {
            for (Map<String, Object> map : list) {
                String appId = MapUtil.getNotNullValue(map, "APPID");
                String nameSpaceId = MapUtil.getNotNullValue(map, "NAMESPACE");
                String itemKey = MapUtil.getNotNullValue(map, "KEYNAME");

                // 获取当前item所属app
                Map<String, Object> app = checkObjExists("appId", appId, appList);
                app.put("appName", MapUtil.getNotNullValue(map, "APPNAME"));
                app.put("appComment", MapUtil.getNotNullValue(map, "APPCOMMENT"));

                // 获取当前item所属nameSpace
                List<Map<String, Object>> nameSpaceList = (List<Map<String, Object>>) app.get("namespace");
                if (nameSpaceList == null) {
                    nameSpaceList = new ArrayList<Map<String, Object>>();
                    app.put("namespace", nameSpaceList);
                }
                if (StringUtil.isEmpty(nameSpaceId)) {
                    continue;
                }
                Map<String, Object> namespace = checkObjExists("namespace", nameSpaceId, nameSpaceList);
                namespace.put("namespaceComment", MapUtil.getNotNullValue(map, "NAMESPACECOMMENT"));

                List<Map<String, Object>> itemList = (List<Map<String, Object>>) namespace.get("item");
                if (itemList == null) {
                    itemList = new ArrayList<Map<String, Object>>();
                    namespace.put("item", itemList);
                }

                if (StringUtil.isEmpty(itemKey)) {
                    continue;
                }
                Map<String, Object> item = new HashMap<String, Object>();
                item.put("keyName", MapUtil.getNotNullValue(map, "KEYNAME"));
                item.put("value", MapUtil.getNotNullValue(map, "VALUE"));
                if (map.get("NEEDRESTART").equals(1)) {
                    item.put("needRestart", true);
                } else {
                    item.put("needRestart", false);
                }
                item.put("itemComment", MapUtil.getNotNullValue(map, "ITEMCOMMENT"));
                itemList.add(item);
            }
        }
        String ja = JSONArray.toJSONString(appList);
        String filePath = generateCreateConfigFile(ConfigJsonUtil.formatJson(ja));
        return filePath;
    }

    /**
     * 导入当前配置到操作表
     *
     * @return 文件路径
     */
    @Override
    @Transactional
    public boolean importConifgData(List<ConfigBeanVo> configBeanVos) {
        if (null == configBeanVos) {
            return false;
        }
        String date = DateUtil.format(new Date(), DateUtil.DATE_TIME_FORMAT);

        for (ConfigBeanVo configBeanVo : configBeanVos) {
            prepareApp(configBeanVo);
            prepareNamespace(configBeanVo);
            //当有父id时，是item项
            if (null != configBeanVo.getParentId()) {
                Item item = itemDao.getItem(configBeanVo.getAppId(),
                        configBeanVo.getNamespace(),
                        configBeanVo.getKeyName());
                //如果不存在，则新增item
                if (null == item) {
                    item = new Item();
                    item.setAppId(configBeanVo.getAppId());
                    item.setComment(configBeanVo.getItemComment());
                    item.setCreateTime(date);
                    item.setKeyName(configBeanVo.getKeyName());
                    item.setValue(configBeanVo.getValue());
                    item.setNamespace(configBeanVo.getNamespace());
                    if (configBeanVo.getNeedRestart()) {
                        item.setNeedRestart(1);
                    } else {
                        item.setNeedRestart(0);
                    }
                    item.setStatus(Status.INSERT);
                    itemDao.addItem(item);
                } else {
                    item.setComment(configBeanVo.getItemComment());
                    item.setCreateTime(date);
                    item.setValue(configBeanVo.getValue());
                    if (configBeanVo.getNeedRestart()) {
                        item.setNeedRestart(1);
                    } else {
                        item.setNeedRestart(0);
                    }
                    item.setStatus(Status.MODIFY);
                    itemDao.updateItem(item);
                }
            }
        }
        return true;
    }

    private void prepareApp(ConfigBeanVo configBeanVo) {
        String date = DateUtil.format(new Date(), DateUtil.DATE_TIME_FORMAT);
        App app = appDao.getApp(configBeanVo.getAppId());
        //如果不存在这个app，则新增
        if (null == app) {
            app = new App();
            app.setAppId(configBeanVo.getAppId());
            app.setAppName(configBeanVo.getAppName());
            app.setComment(configBeanVo.getAppComment());
            app.setCreateTime(date);
            app.setStatus(Status.INSERT);
            appDao.addApp(app);
        } else {
            //如果存在，则更新
            app.setAppName(configBeanVo.getAppName());
            app.setComment(configBeanVo.getAppComment());
            app.setUpdateTime(date);
            app.setStatus(Status.MODIFY);
            appDao.updateApp(app);
        }
    }

    private void prepareNamespace(ConfigBeanVo configBeanVo) {
        //当是ems代理的配置子项时，不再修改命名空间
        if (configBeanVo.getAppId().equals(EMS_APP_ID) && configBeanVo.getParentId() != null) {
            return;
        }
        String date = DateUtil.format(new Date(), DateUtil.DATE_TIME_FORMAT);
        Namespace namespace = namespaceDao.getNamespace(configBeanVo.getAppId(), configBeanVo.getNamespace());
        //如果不存在这个namespace，则新增
        if (null == namespace) {
            namespace = new Namespace();
            namespace.setAppId(configBeanVo.getAppId());
            namespace.setComment(configBeanVo.getNamespaceComment());
            namespace.setCreateTime(date);
            namespace.setNamespace(configBeanVo.getNamespace());
            namespace.setStatus(Status.INSERT);
            namespaceDao.addNamespace(namespace);
        } else {
            //如果存在，则更新
            namespace.setComment(configBeanVo.getNamespaceComment());
            namespace.setUpdateTime(date);
            namespace.setStatus(Status.MODIFY);
            namespaceDao.updateNamespace(namespace);
        }
    }

    /**
     * 导入app数据
     *
     * @param appConfig appConfig
     * @param isDelete  isdelete
     */
    private void importAppData(Map<String, Object> appConfig, Boolean isDelete, String date) {
        String appId = MapUtil.getNotNullValue(appConfig, "appId");
        String appName = MapUtil.getNotNullValue(appConfig, "appName");
        String appComment = MapUtil.getNotNullValue(appConfig, "appComment");
        App app = appDao.getApp(appId);
        isDelete = isDelete == null ? false : isDelete;

        boolean isNew = false;
        if (app == null) {
            app = new App();
            app.setAppId(appId);
            app.setStatus(Status.INSERT);
            app.setCreateTime(date);
            isNew = true;
        } else {
            // 判断插入的数据是否有变更，有则为modify，没有则为publicshed
            if (appName.equals(app.getAppName()) && appComment.equals(app.getComment())) {
                app.setStatus(Status.PUBLISHED + app.getStatus());
            } else {
                app.setStatus(Status.MODIFY);
            }
        }
        app.setAppName(appName);
        app.setComment(appComment);

        if (isDelete) {
            app.setStatus(Status.DELETE);
        }
        if (app.getStatus() != Status.PUBLISHED) {
            app.setUpdateTime(date);
        }

        if (isNew) {
            appDao.addApp(app);
        } else {
            appDao.updateApp(app);
        }
    }

    /**
     * 导入namespace数据
     *
     * @param config   config
     * @param appId    appid
     * @param isDelete isdelete
     */
    private void importNamespaceData(Map<String, Object> config, String appId, Boolean isDelete, String date) {
        String namespaceId = MapUtil.getNotNullValue(config, "namespace");
        String namespaceComment = MapUtil.getNotNullValue(config, "namespaceComment");
        Namespace namespace = namespaceDao.getNamespace(appId, namespaceId);

        boolean isNew = false;
        if (namespace == null) {
            namespace = new Namespace();
            namespace.setAppId(appId);
            namespace.setNamespace(namespaceId);
            namespace.setStatus(Status.INSERT);
            namespace.setCreateTime(date);
            isNew = true;
        } else {
            if (namespaceComment.equals(namespace.getComment())) {
                namespace.setStatus(Status.PUBLISHED + namespace.getStatus());
            } else {
                namespace.setStatus(Status.MODIFY);
            }

        }
        namespace.setComment(namespaceComment);

        if (isDelete) {
            namespace.setStatus(Status.DELETE);
        }

        if (namespace.getStatus() != Status.PUBLISHED) {
            namespace.setUpdateTime(date);
        }

        if (isNew) {
            namespaceDao.addNamespace(namespace);
        } else {
            namespaceDao.updateNamespace(namespace);
        }
    }

    /**
     * 导入item数据
     *
     * @param config      config
     * @param appId       appid
     * @param namespaceId namespaceId
     * @param isDelete    isdelete
     */
    private void importItemData(Map<String, Object> config, String appId, String namespaceId, Boolean isDelete,
                                String date) {
        String itemKey = MapUtil.getNotNullValue(config, "keyName");
        String itemValue = MapUtil.getNotNullValue(config, "value");
        String itemComment = MapUtil.getNotNullValue(config, "itemComment");
        Integer needRestart = (Integer) config.get("needRestart");
        needRestart = needRestart == null ? 0 : needRestart;

        Item item = itemDao.getItem(appId, namespaceId, itemKey);

        boolean isNew = false;
        if (item == null) {
            item = new Item();
            item.setAppId(appId);
            item.setNamespace(namespaceId);
            item.setKeyName(itemKey);
            item.setStatus(Status.INSERT);
            item.setCreateTime(date);
            isNew = true;
        } else {
            if (itemValue.equals(item.getValue()) && itemComment.equals(item.getComment())
                    && needRestart.equals(item.getNeedRestart())) {

                item.setStatus(Status.PUBLISHED + item.getStatus());
            } else {
                item.setStatus(Status.MODIFY);
            }

        }
        item.setComment(itemComment);
        item.setValue(itemValue);

        if (isDelete) {
            item.setStatus(Status.DELETE);
        }

        if (item.getStatus() != Status.PUBLISHED) {
            item.setUpdateTime(date);
        }
        if (isNew) {
            itemDao.addItem(item);
        } else {
            itemDao.updateItem(item);
        }
    }

    /**
     * 生成文件
     *
     * @param str json字符串
     * @return str
     */
    private String generateCreateConfigFile(String str) {
        String date = DateUtil.format(new Date(), DATE_FORMAT);
        String configFilePath = ConfigJsonUtil.getConfigOutputPath();
        File configPathDir = new File(configFilePath);
        if (!configPathDir.exists() || !configPathDir.isDirectory()) {
            configPathDir.mkdirs();
        }
        String fileName = "config_" + date + ".json";
        File file = new File(configFilePath + fileName);
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                TRACER.error("create config file failed", e);
            }
        }
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(str.getBytes(CharSetConstant.UTF8));
            fos.flush();
            fos.close();
        } catch (Exception e) {
            TRACER.error("write config file failed", e);
        }

        return file.getPath();
    }

    /**
     * 检查当前list中是否有键为key,值为value的map，有则返回，没有则新建
     *
     * @param key   List中的map对象的键key
     * @param value List中的map对象的对应键key的value
     * @param list  List<Map<String, Object>>
     * @return 返回当前list中主键为key的map
     */
    private Map<String, Object> checkObjExists(String key, String value, List<Map<String, Object>> list) {
        Map<String, Object> result = null;
        for (Map<String, Object> map : list) {
            String appId = (String) map.get(key);
            if (value != null && value.equals(appId)) {
                result = map;
                break;
            }
        }
        if (result == null) {
            result = new HashMap<String, Object>();
            result.put(key, value);
            list.add(result);
        }
        return result;
    }

}
